<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>Gtk中的文本视图（GtkTexView Widget）</title>
<link rel="stylesheet" href="/cfg/format.css" type="text/css">
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<meta name="keywords" content="GUI, GtkTexView Widget, GTK+ library, C">
<meta nam="description" content="GtkTexView Widget">
<meta name="language" content="zh, en">
<meta name="author" content="Jan Bodnar">
<meta name="distribution" content="global">

<script type="text/javascript" src="/lib/jquery.js"></script>
<script type="text/javascript" src="/lib/common.js"></script>

</head>

<body>

<div class="container">

<div id="wide_ad" class="ltow">
<script type="text/javascript"><!--
google_ad_client = "pub-9706709751191532";
/* 160x600, August 2011 */
google_ad_slot = "2484182563";
google_ad_width = 160;
google_ad_height = 600;
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script>
</div>

<div class="content">

<a href="/" title="Home">Home</a>&nbsp;
<a href="..">Contents</a>


<h1>Gtk中的文本视图（GtkTexView Widget）</h1>


<p>
在本章的Gtk+程序设计教程中，我们将重点介绍 GtkTexView 构件。 
</p>

<div class="center"> 
<script type="text/javascript"><!--
google_ad_client = "pub-9706709751191532";
/* horizontal */
google_ad_slot = "1734478269";
google_ad_width = 468;
google_ad_height = 60;
//-->
</script> 
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"> 
</script> 
</div>

<p>
<b class="keyword">GtkTexView</b> w构件被常常用来显示和编辑多行的文本。正如我们一再提到的，
<b class="keyword">GtkTexBuffer</b> 构件也是给予MVC的设计。GtkTextView 就是显示（view）元素而 GtkTexBuffer 则代表了model 元素。
GtkTexBuffer 常常被用来处理文本数据。<b class="keyword">GtkTextTag</b>则是一种被用于文本的属性。 
<b class="keyword">GtkTextIter</b>则是代表了两个字符之间的空隙。那么很好理解，文本的排版操作多用iterators。 
</p>

<h2>简单的例子（Simple example）</h2>

<p>
在我们的第一个例子中，我们将向大家展示GtkTexView 的一些功能。我们还将教大家怎么样去应用各种各样的文本标记（ tags ）。 
</p>

<pre class="code">
#include &lt;gtk/gtk.h&gt;


int main( int argc, char *argv[])
{

  GtkWidget *window;
  GtkWidget *view;
  GtkWidget *vbox;
  
  GtkTextBuffer *buffer;
  GtkTextIter start, end;
  GtkTextIter iter;

  gtk_init(&amp;argc, &amp;argv);

  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
  gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
  gtk_window_set_default_size(GTK_WINDOW(window), 250, 200);
  gtk_window_set_title(GTK_WINDOW(window), "TextView");
  gtk_container_set_border_width(GTK_CONTAINER(window), 5);
  GTK_WINDOW(window)->allow_shrink = TRUE;

  vbox = gtk_vbox_new(FALSE, 0);
  view = gtk_text_view_new();
  gtk_box_pack_start(GTK_BOX(vbox), view, TRUE, TRUE, 0);

  buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(view));

  gtk_text_buffer_create_tag(buffer, "gap",
        "pixels_above_lines", 30, NULL);

  gtk_text_buffer_create_tag(buffer, "lmarg", 
      "left_margin", 5, NULL);
  gtk_text_buffer_create_tag(buffer, "blue_fg", 
      "foreground", "blue", NULL); 
  gtk_text_buffer_create_tag(buffer, "gray_bg", 
      "background", "gray", NULL); 
  gtk_text_buffer_create_tag(buffer, "italic", 
      "style", PANGO_STYLE_ITALIC, NULL);
  gtk_text_buffer_create_tag(buffer, "bold", 
      "weight", PANGO_WEIGHT_BOLD, NULL);

  gtk_text_buffer_get_iter_at_offset(buffer, &amp;iter, 0);

  gtk_text_buffer_insert(buffer, &amp;iter, "Plain text\n", -1);
  gtk_text_buffer_insert_with_tags_by_name(buffer, &amp;iter, 
        "Colored Text\n", -1, "blue_fg", "lmarg",  NULL);
  gtk_text_buffer_insert_with_tags_by_name (buffer, &amp;iter, 
        "Text with colored background\n", -1, "lmarg", "gray_bg", NULL);

  gtk_text_buffer_insert_with_tags_by_name (buffer, &amp;iter, 
        "Text in italics\n", -1, "italic", "lmarg",  NULL);

  gtk_text_buffer_insert_with_tags_by_name (buffer, &amp;iter, 
        "Bold text\n", -1, "bold", "lmarg",  NULL);

  gtk_container_add(GTK_CONTAINER(window), vbox);

  g_signal_connect_swapped(G_OBJECT(window), "destroy",
        G_CALLBACK(gtk_main_quit), G_OBJECT(window));

  gtk_widget_show_all(window);

  gtk_main();

  return 0;
}
</pre>

<p>
这个例子展示了如何利用各种各样的文本标记（ <b class="keyword">GtkTextTags</b>）来显示文本。 
</p>

<pre class="explanation">
 view = gtk_text_view_new();
</pre>

<p>
生成一个<b class="keyword">GtkTextView</b>。
</p>

<pre class="explanation">
 gtk_text_buffer_create_tag(buffer, "blue_fg", 
     "foreground", "blue", NULL); 
</pre>

<p>
这就是一个运用 <b class="keyword">GtkTextTag</b>的例子，这个标记改变了文本的颜色。 
</p>

<pre class="explanation">
 gtk_text_buffer_insert_with_tags_by_name(buffer, &amp;iter, 
       "Colored Text\n", -1, "blue_fg", "lmarg",  NULL);
</pre>

<p>
这个代码插入了一些文本，并运用了一个特殊的文本标记<b class="keyword">blue_fg</b>。  
</p>

<br>
<img src="../images/simpletextview.png" alt="Simple TextView">
<div class="figure">Figure: Simple TextView</div>
<br>

<h2>行和栏（Lines and Columns）</h2>

<p>
在接下来的示例中将要显示文本编辑光标目前处于的行数和列数。 
</p>

<pre class="code">
#include &lt;gtk/gtk.h&gt;

update_statusbar(GtkTextBuffer *buffer,
    GtkStatusbar  *statusbar)
{
  gchar *msg;
  gint row, col;
  GtkTextIter iter;

  gtk_statusbar_pop(statusbar, 0); 

  gtk_text_buffer_get_iter_at_mark(buffer,
      &amp;iter, gtk_text_buffer_get_insert(buffer));

  row = gtk_text_iter_get_line(&amp;iter);
  col = gtk_text_iter_get_line_offset(&amp;iter);

  msg = g_strdup_printf("Col %d Ln %d", col+1, row+1);

  gtk_statusbar_push(statusbar, 0, msg);

  g_free(msg);
}

static void
mark_set_callback(GtkTextBuffer *buffer,
    const GtkTextIter *new_location, GtkTextMark *mark,
    gpointer data)
{
  update_statusbar(buffer, GTK_STATUSBAR(data));
}


int main( int argc, char *argv[])
{

  GtkWidget *window;
  GtkWidget *vbox;

  GtkWidget *toolbar;
  GtkWidget *view;
  GtkWidget *statusbar;
  GtkToolItem *exit;
  GtkTextBuffer *buffer;

  gtk_init(&amp;argc, &amp;argv);

  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
  gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
  gtk_window_set_default_size(GTK_WINDOW(window), 250, 200);
  gtk_window_set_title(GTK_WINDOW(window), "lines &amp; cols");

  vbox = gtk_vbox_new(FALSE, 0);
  gtk_container_add(GTK_CONTAINER(window), vbox);

  toolbar = gtk_toolbar_new();
  gtk_toolbar_set_style(GTK_TOOLBAR(toolbar), GTK_TOOLBAR_ICONS);

  exit = gtk_tool_button_new_from_stock(GTK_STOCK_QUIT);
  gtk_toolbar_insert(GTK_TOOLBAR(toolbar), exit, -1);

  gtk_box_pack_start(GTK_BOX(vbox), toolbar, FALSE, FALSE, 5);

  view = gtk_text_view_new();
  gtk_box_pack_start(GTK_BOX(vbox), view, TRUE, TRUE, 0);
  gtk_widget_grab_focus(view);

  buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(view));

  statusbar = gtk_statusbar_new();
  gtk_box_pack_start(GTK_BOX(vbox), statusbar, FALSE, FALSE, 0);

  g_signal_connect(G_OBJECT(exit), "clicked", 
        G_CALLBACK(gtk_main_quit), NULL);

  g_signal_connect(buffer, "changed",
        G_CALLBACK(update_statusbar), statusbar);

  g_signal_connect_object(buffer, "mark_set", 
        G_CALLBACK(mark_set_callback), statusbar, 0);

  g_signal_connect_swapped(G_OBJECT(window), "destroy",
        G_CALLBACK(gtk_main_quit), NULL);

  gtk_widget_show_all(window);

  update_statusbar(buffer, GTK_STATUSBAR (statusbar));

  gtk_main();

  return 0;
}
</pre>

<p>
在上面的代码示例中，我们完成了在状态栏中显示当前文本编辑光标所处于的行和列数。 
</p>

<pre class="explanation">
 view = gtk_text_view_new();
</pre>

<p>
生成一了 <b class="keyword">GtkTextView</b>构件。
</p>

<pre class="explanation">
 g_signal_connect(buffer, "changed",
       G_CALLBACK(update_statusbar), statusbar);
</pre>

<p>
如果我们要更改文本，我们只需要调用回调函数 <b class="keyword">update_statusbar()</b> 就可以了。
</p>

<pre class="explanation">
 g_signal_connect_object(buffer, "mark_set", 
       G_CALLBACK(mark_set_callback), statusbar, 0);
</pre>

<p>
当光标在移动的时候， <b class="keyword">mark_set</b> 信号就被发射出去了。
</p>

<pre class="explanation">
 gtk_statusbar_pop(statusbar, 0); 
</pre>

<p>
这段代码功能是清除了先前的任何一些状态栏中的信息。
</p>

<pre class="explanation">
 gtk_text_buffer_get_iter_at_mark(buffer,
     &amp;iter, gtk_text_buffer_get_insert(buffer));

 row = gtk_text_iter_get_line(&amp;iter);
 col = gtk_text_iter_get_line_offset(&amp;iter);
</pre>

<p>
显然上面的代码是在获取当前所处于的行号与列号。 
</p>

<pre class="explanation">
 msg = g_strdup_printf("Col %d Ln %d", col+1, row+1);
</pre>

<p>
上面的代码准备好，状态栏中显示出来的行号与列号的内容。
</p>


<pre class="explanation">
 gtk_statusbar_push(statusbar, 0, msg);
</pre>

<p>
然后，我们就在状态栏上显示文本。 
</p>

<br>
<img src="../images/linesandcols.png" alt="Lines &amp; Columns">
<div class="figure">Figure: Lines &amp; Columns</div>
<br>


<h2>监测&amp; 突显（Search &amp; Highlight）</h2>

<p>
在接下来的例子中，我们将要在 <b class="keyword">GtkTextBuffer</b>中做一些监测的工作。我们还将把一些文本的内容进行“突显”处理。

</p>

<pre class="code">
#include &lt;gtk/gtk.h&gt;
#include &lt;gdk/gdkkeysyms.h&gt;



gboolean key_pressed(GtkWidget * window,
    GdkEventKey* event, GtkTextBuffer *buffer) {

  GtkTextIter start_sel, end_sel;
  GtkTextIter start_find, end_find;
  GtkTextIter start_match, end_match;
  gboolean selected;	
  gchar *text;		  
  


  if ((event->type == GDK_KEY_PRESS) &amp;&amp; 
     (event->state &amp; GDK_CONTROL_MASK)) {

    switch (event->keyval)
    {
      case GDK_m :
        selected = gtk_text_buffer_get_selection_bounds(buffer, 
            &amp;start_sel, &amp;end_sel);
      if (selected) {
        gtk_text_buffer_get_start_iter(buffer, &amp;start_find);
        gtk_text_buffer_get_end_iter(buffer, &amp;end_find);

        gtk_text_buffer_remove_tag_by_name(buffer, "gray_bg", 
            &amp;start_find, &amp;end_find);  
        text = (char *) gtk_text_buffer_get_text(buffer, &amp;start_sel,
            &amp;end_sel, FALSE);

        while ( gtk_text_iter_forward_search(&amp;start_find, text, 
                GTK_TEXT_SEARCH_TEXT_ONLY | 
                GTK_TEXT_SEARCH_VISIBLE_ONLY, 
                &amp;start_match, &amp;end_match, NULL) ) {

          gtk_text_buffer_apply_tag_by_name(buffer, "gray_bg", 
              &amp;start_match, &amp;end_match);
          int offset = gtk_text_iter_get_offset(&amp;end_match);
          gtk_text_buffer_get_iter_at_offset(buffer, 
              &amp;start_find, offset);
        }

        g_free(text);
      }

      break;

      case GDK_r:
        gtk_text_buffer_get_start_iter(buffer, &amp;start_find);
        gtk_text_buffer_get_end_iter(buffer, &amp;end_find);
      
        gtk_text_buffer_remove_tag_by_name(buffer, "gray_bg", 
            &amp;start_find, &amp;end_find);  
      break;
    }
  }

  return FALSE;
}


int main( int argc, char *argv[])
{

  GtkWidget *window;
  GtkWidget *view;
  GtkWidget *vbox;
  
  GtkTextBuffer *buffer;
  GtkTextIter start, end;
  GtkTextIter iter;

  gtk_init(&amp;argc, &amp;argv);

  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
  gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
  gtk_window_set_default_size(GTK_WINDOW(window), 250, 200);
  gtk_window_set_title(GTK_WINDOW(window), "Search &amp; Highlight");
  gtk_container_set_border_width(GTK_CONTAINER(window), 5);
  GTK_WINDOW(window)->allow_shrink = TRUE;

  vbox = gtk_vbox_new(FALSE, 0);
  view = gtk_text_view_new();
  gtk_widget_add_events(view, GDK_BUTTON_PRESS_MASK);
  gtk_box_pack_start(GTK_BOX(vbox), view, TRUE, TRUE, 0);

  buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(view));
  gtk_text_buffer_create_tag(buffer, "gray_bg", 
      "background", "gray", NULL); 
  gtk_container_add(GTK_CONTAINER(window), vbox);

  g_signal_connect_swapped(G_OBJECT(window), "destroy",
        G_CALLBACK(gtk_main_quit), G_OBJECT(window));

  g_signal_connect(G_OBJECT(window), "key-press-event",
        G_CALLBACK(key_pressed), buffer);

  gtk_widget_show_all(window);

  gtk_main();

  return 0;
}
</pre>

<p>
在我们的示例中，我们用到了键盘的快捷键。Ctrl + M 是用来突显我们当前所选取的文本内容。Ctrl + R 则是用来取消上面的操作。 
</p>

<pre class="explanation">
 gtk_text_buffer_create_tag(buffer, "gray_bg", 
     "background", "gray", NULL); 
</pre>

<p>
我们在例子中会再次用到  <b class="keyword">GtkTextTag</b> 。这个标记可以使文本的背景反白。 

</p>

<pre class="explanation">
 selected = gtk_text_buffer_get_selection_bounds(buffer, 
     &amp;start_sel, &amp;end_sel);
</pre>

<p>
这里我们得到我们选中的文本所具有的起始和终点位置。
</p>


<pre class="explanation">
 gtk_text_buffer_get_start_iter(buffer, &amp;start_find);
 gtk_text_buffer_get_end_iter(buffer, &amp;end_find);
</pre>

<p>
我们得到了文本缓冲区（text buffer）的起始和终点位置。 
</p>


<pre class="explanation">
 gtk_text_buffer_remove_tag_by_name(buffer, "gray_bg", 
     &amp;start_find, &amp;end_find);  
</pre>

<p>
上面就是，把先前的标记去处。 
</p>

<pre class="explanation">
 text = (char *) gtk_text_buffer_get_text(buffer, &amp;start_sel,
     &amp;end_sel, FALSE);
</pre>

<p>
接着我们得到了所选择的文本内容，我们将要进行监测。 
</p>

<pre class="explanation">
 while ( gtk_text_iter_forward_search(&amp;start_find, text, 
         GTK_TEXT_SEARCH_TEXT_ONLY | 
         GTK_TEXT_SEARCH_VISIBLE_ONLY, 
         &amp;start_match, &amp;end_match, NULL) ) {

   gtk_text_buffer_apply_tag_by_name(buffer, "gray_bg", 
       &amp;start_match, &amp;end_match);
   int offset = gtk_text_iter_get_offset(&amp;end_match);
   gtk_text_buffer_get_iter_at_offset(buffer, 
       &amp;start_find, offset);
 }
</pre>

<p>
这段代码将检测所有我们所选择的文本后的所发生的事件，一旦发现与我们定义的内容有匹配就应用我们设定好的标记。在匹配工作完成之后，单词的尾端将将被成下次监视操作的首端。 
</p>


<br>
<img src="../images/searchhighlight.png" alt="Search &amp; Highlight">
<div class="figure">Figure: Search &amp; Highlight</div>
<br>

<div class="center"> 
<script type="text/javascript"><!--
google_ad_client = "pub-9706709751191532";
/* horizontal */
google_ad_slot = "1734478269";
google_ad_width = 468;
google_ad_height = 60;
//-->
</script> 
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"> 
</script> 
</div>


<div class="botNav, center">
<span class="botNavItem"><a href="/">Home</a></span> ‡ <span class="botNavItem"><a href="..">Contents</a></span> ‡
<span class="botNavItem"><a href="#">Top of Page</a></span>
</div>


<div class="footer">
<div class="signature">
<a href="/">ZetCode</a> last modified March 29, 2008  <span class="copyright">&copy; 2007 - 2012 Jan Bodnar</span>
<span class="copyright">Translation &copy; 2008 chinalmy</span>
</div>
</div>

</div> <!-- content -->

</div> <!-- container -->

</body>
</html>

